﻿#include <QDebug>
//#include <intsafe.h>
#include "mainwindow.h"
#include "fcs.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
	initPointers();
	initCom();
}

MainWindow::~MainWindow()
{
	RELEASE_POINTER_RESOURCE(m_comset)
	RELEASE_TIMER_RESOURCE(obotimer)
	RELEASE_COM_RESOURCE(myCom)
	RELEASE_POINTER_RESOURCE(ui)
}

void MainWindow::initPointers()
{
	SET_POINTER_NULL(ui)
	SET_POINTER_NULL(myCom)
	SET_POINTER_NULL(obotimer)
	SET_POINTER_NULL(m_comset)
}

void MainWindow::changeEvent(QEvent *e)
{
	QMainWindow::changeEvent(e);
	switch (e->type()){
	case QEvent::LanguageChange:
		ui->retranslateUi(this);
		break;
	default:
		break;
	}
}

void MainWindow::initCom()
{
	ui = new Ui::MainWindow;
	ui->setupUi(this);

	setActionsEnabled(false);
	ui->delayspinBox->setEnabled(false);
    ui->sendmsgBtn->setEnabled(false);
	ui->obocheckBox->setEnabled(false);

    //初始化连续发送计时器计时间隔
	obotimerdly = OBO_TIMER_INTERVAL;

    //设置连续发送计时器
	obotimer = new QTimer(this);
	connect(obotimer, SIGNAL(timeout()), this, SLOT(sendMsg()));

	foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
		ui->portNameComboBox->addItem(info.portName());
	}

	ui->statusBar->showMessage(QObject::tr("Welcome Using DeluCom!"));
	ui->btnRecv->hide();

    //load default parameters
	m_comset = new QSettings("comdebugger.ini", QSettings::IniFormat);
	if (m_comset){
		ui->portNameComboBox->setCurrentIndex(m_comset->value("ComSettings/ComName").toInt());
		ui->baudRateComboBox->setCurrentIndex(m_comset->value("ComSettings/BaudRate").toInt());
		ui->dataBitsComboBox->setCurrentIndex(m_comset->value("ComSettings/DataBit").toInt());
		ui->parityComboBox->setCurrentIndex(m_comset->value("ComSettings/Parity").toInt());
		ui->stopBitsComboBox->setCurrentIndex(m_comset->value("ComSettings/StopBit").toInt());
	}
}

void MainWindow::setActionsEnabled(bool status)
{
	ui->actionSave->setEnabled(status);
	ui->actionClose->setEnabled(status);
	ui->actionLoadfile->setEnabled(status);
	ui->actionCleanPort->setEnabled(status);
	ui->actionWriteToFile->setEnabled(status);
}

void MainWindow::setComboBoxEnabled(bool status)
{
	ui->portNameComboBox->setEnabled(status);
	ui->baudRateComboBox->setEnabled(status);
	ui->dataBitsComboBox->setEnabled(status);
	ui->parityComboBox->setEnabled(status);
	ui->stopBitsComboBox->setEnabled(status);
}

void MainWindow::on_actionOpen_triggered()
{
    QString portName = ui->portNameComboBox->currentText();
#ifdef Q_OS_LINUX
	myCom = new QSerialPort("/dev/" + portName);
#elif defined (Q_OS_WIN)
	myCom = new QSerialPort();
#endif
	connect(myCom, SIGNAL(readyRead()), this, SLOT(readMyCom()));

	myCom->setPortName(portName);
	myCom->setBaudRate((QSerialPort::BaudRate)(ui->baudRateComboBox->currentText().toInt()));
	myCom->setDataBits((QSerialPort::DataBits)(ui->dataBitsComboBox->currentText().toInt()));

	switch(ui->parityComboBox->currentIndex()){
	case 0:
		myCom->setParity(QSerialPort::NoParity);
		break;
	case 1:
		myCom->setParity(QSerialPort::OddParity);
		break;
	case 2:
		myCom->setParity(QSerialPort::EvenParity);
		break;
	default:
		myCom->setParity(QSerialPort::NoParity);
		qDebug("set to default : NoParity");
		break;
	}

	switch(ui->stopBitsComboBox->currentIndex())	{
	case 0:
		myCom->setStopBits(QSerialPort::OneStop);
		break;
	case 1:
#ifdef Q_OS_WIN
		myCom->setStopBits(QSerialPort::OneAndHalfStop);
#endif
		break;
	case 2:
		myCom->setStopBits(QSerialPort::TwoStop);
		break;
	default:
		myCom->setStopBits(QSerialPort::OneStop);
		qDebug("set to default : OneStop");
		break;
	}

	myCom->setFlowControl(QSerialPort::NoFlowControl);

	if (myCom->open(QIODevice::ReadWrite)){
		ui->sendmsgBtn->setEnabled(true);
		setComboBoxEnabled(false);
		ui->textEditSend->setEnabled(true);
		ui->actionOpen->setEnabled(false);
		ui->textEditSend->setFocus();
		ui->obocheckBox->setEnabled(true);
		setActionsEnabled(true);
	}
	else{
		QMessageBox::critical(this, tr("Error"), tr("can't open ") + portName + tr("\ndevice is occupied or isn't exist!"), QMessageBox::Ok);
		return;
	}

	saveComDefaultConfig();
	ui->statusBar->showMessage(tr("Open Com Success"));
}


void MainWindow::on_actionClose_triggered()
{
	if (myCom != NULL){
		if (myCom->isOpen())
			myCom->close();

		delete myCom;
		myCom = NULL;
	}

	if (ui->sendmsgBtn->text() == tr("pause")){
		obotimer->stop();
		ui->sendmsgBtn->setText(tr("send"));
		ui->sendmsgBtn->setIcon(QIcon(":new/prefix1/src/send.png"));
		ui->obocheckBox->setChecked(false);
	}
	ui->sendmsgBtn->setEnabled(false);
	setComboBoxEnabled(true);
	ui->actionOpen->setEnabled(true);
	//ui->textEditSend->setEnabled(false);
	ui->obocheckBox->setEnabled(false);
	setActionsEnabled(false);
	ui->actionWriteToFile->setChecked(false);
	ui->statusBar->showMessage(tr("Com is closed"));
}

void MainWindow::on_actionAbout_triggered()
{
	aboutdlg.show();

	int x =this->x() + (this->width() - aboutdlg.width()) / 2;
	int y =this->y() + (this->height() - aboutdlg.height()) / 2;
	aboutdlg.move(x, y);
	ui->statusBar->showMessage(tr("About DeluCom"));
}

void MainWindow::readMyCom()
{
	QByteArray temp = myCom->readAll();
	QString buf;

	if(!temp.isEmpty()){
		ui->textBrowserRecv->setTextColor(Qt::black);
        if (ui->ccradioButton->isChecked())	{
			buf = temp;
        } else if (ui->chradioButton->isChecked()) {
            QString str;
            for (int i = 0; i < temp.count(); i++)
            {
                QString s;
                s.sprintf("%02X ", (unsigned char)temp.at(i));
                buf += s;
            }
		}

        if (!write2fileName.isEmpty()) {
			QFile file(write2fileName);
            //如果打开失败则给出提示并退出函数
            if (!file.open(QFile::Append | QIODevice::Text)){
                QMessageBox::warning(this, tr("Write File"), \
                                     tr("open file %1 failed, can't write\n%2").\
                                     arg(write2fileName).\
                                     arg(file.errorString()), QMessageBox::Ok);
				return;
			}
			QTextStream out(&file);
			out<<buf;
			file.close();
		}

		ui->textBrowserRecv->setText(ui->textBrowserRecv->document()->toPlainText() + buf +"\n");
		QTextCursor cursor = ui->textBrowserRecv->textCursor();
		cursor.movePosition(QTextCursor::End);
		ui->textBrowserRecv->setTextCursor(cursor);

		ui->recvBytesLcdNumber->display(ui->recvBytesLcdNumber->value() + temp.size());
		ui->statusBar->showMessage(tr("read %1 bytes data success").arg(temp.size()));
	}
}

void MainWindow::sendMsg()
{
	QByteArray buf;
    if (ui->chradioButton2->isChecked()) {
		QString str;
		bool ok;
		char data;
		QStringList list;
		str = ui->textEditSend->toPlainText();
        list = str.split(" ");
		for (int i = 0; i < list.count(); i++){
			if (list.at(i) == " ")
				continue;
			if (list.at(i).isEmpty())
				continue;
			data = (char)list.at(i).toInt(&ok, 16);
			if (!ok){
				QMessageBox::information(this, tr("Hint"), tr("data format error锟斤拷"), QMessageBox::Ok);
				if(obotimer != NULL)
					obotimer->stop();
				ui->sendmsgBtn->setText(tr("send"));
				ui->sendmsgBtn->setIcon(QIcon(":new/prefix1/src/send.png"));
				return;
			}
			buf.append(data);
		}
    } else {
#if QT_VERSION < 0x050000
		buf = ui->textEditSend->toPlainText().toAscii();
#else
		buf = ui->textEditSend->toPlainText().toLocal8Bit();
#endif
	}

	myCom->write(buf);
	ui->sendBytesLcdNumber->display(ui->sendBytesLcdNumber->value() + buf.size());
	ui->statusBar->showMessage(tr("send data success"));
	ui->textBrowserRecv->setTextColor(Qt::lightGray);
}

void MainWindow::on_sendmsgBtn_clicked()
{
    //如果当前正在连续发送数据, 暂停发送
	if (ui->sendmsgBtn->text() == tr("pause")){
		obotimer->stop();
		ui->sendmsgBtn->setText(tr("send"));
		ui->sendmsgBtn->setIcon(QIcon(":new/prefix1/src/send.png"));
		return;
	}
    //如果发送数据为空，给出提示并返回
	if (ui->textEditSend->toPlainText().isEmpty()){
		QMessageBox::information(this, tr("Hint"), tr("no data need to send"), QMessageBox::Ok);
		return;
	}

    //如果不是连续发送
	if (!ui->obocheckBox->isChecked()){
		ui->textEditSend->setFocus();
		//锟斤拷锟斤拷锟斤拷锟斤拷
		sendMsg();
    } else {//连续发送
		obotimer->start(obotimerdly);
		ui->sendmsgBtn->setText(tr("pause"));
		ui->sendmsgBtn->setIcon(QIcon(":new/prefix1/src/pause.png"));
	}
}

void MainWindow::on_clearRecvBtn_clicked()
{
	ui->textBrowserRecv->clear();
	ui->statusBar->showMessage(tr("receive record is cleared"));
}

void MainWindow::on_clearSendBtn_clicked()
{
	ui->textEditSend->clear();
	ui->statusBar->showMessage(tr("send record is cleared"));
}

void MainWindow::on_btnResetRecvLcd_clicked()
{
	if (ui->recvBytesLcdNumber->value()==0){
		QMessageBox::information(this, tr("Hint"), tr("already cleared"), QMessageBox::Ok);
	}
	else{
		ui->recvBytesLcdNumber->display(0);
		ui->statusBar->showMessage(tr("receive LCD already cleared"));
	}
}

void MainWindow::on_btnResetSendLcd_clicked()
{
	if (ui->sendBytesLcdNumber->value()==0){
		QMessageBox::information(this, tr("Hint"), tr("already cleared"), QMessageBox::Ok);
	}
	else{
		ui->sendBytesLcdNumber->display(0);
		ui->statusBar->showMessage(tr("send LCD already cleared"));
	}
}

void MainWindow::on_actionClearRecvBytes_triggered()
{
	on_clearRecvBtn_clicked();
	on_btnResetRecvLcd_clicked();
}

void MainWindow::on_actionClearSendBytes_triggered()
{
	on_clearSendBtn_clicked();
	on_btnResetSendLcd_clicked();
}

void MainWindow::on_obocheckBox_clicked()
{
	if (ui->obocheckBox->isChecked()){
		ui->delayspinBox->setEnabled(true);
        //检查是否有数据，如有则启动定时器
		ui->statusBar->showMessage(tr("start send continuously"));
    } else {//若定时器已经启动则关闭它
		ui->delayspinBox->setEnabled(false);
		ui->statusBar->showMessage(tr("stop send continuously"));
	}
}

void MainWindow::on_actionSave_triggered()
{
	if (ui->textEditSend->toPlainText().isEmpty()){
		QMessageBox::information(this, tr("Hint"), tr("no data need save! please input data in the sending area"), QMessageBox::Ok);
		return;
	}

	QString filename = QFileDialog::getSaveFileName(this, tr("Save As"), tr("noname.txt"));
	QFile file(filename);
    //如果用户取消了保存则直接退出函数
	if (file.fileName().isEmpty())
		return;

    //如果打开失败则给出提示并退出函数
	if (!file.open(QFile::WriteOnly | QIODevice::Text)){
        QMessageBox::warning(this, tr("Save File"), \
                             tr("open file %1 failed, can't save\n%2").\
                             arg(filename).\
                             arg(file.errorString()), QMessageBox::Ok);
		return;
	}
    //写数据到文件
	QTextStream out(&file);
	out<<ui->textEditSend->toPlainText();
	file.close();
    //修改窗口标题为保存文件路径
	setWindowTitle("saved: " + QFileInfo(filename).canonicalFilePath());
}

void MainWindow::on_actionExit_triggered()
{
	this->close();
}

//锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷时锟斤拷锟斤拷
void MainWindow::on_delayspinBox_valueChanged(int )
{
	obotimerdly = ui->delayspinBox->value();
}

void MainWindow::on_actionLoadfile_triggered()
{
	QString filename = QFileDialog::getOpenFileName(this, tr("Read File"), 0, tr("save as (*.txt *.log)"));  //锟斤拷痈锟斤拷锟斤拷锟侥硷拷锟斤拷锟斤拷
	QFile file(filename);

	if (file.fileName().isEmpty()){
		return;
	}

	if (!file.open(QFile::ReadOnly | QIODevice::Text)){
		QMessageBox::warning(this, tr("Open Failed"),
			tr("Sorry! DeluCom can't open this file, maybe no right. you can try chmod to modify right."),
			QMessageBox::Ok);
		return;
	}

	if (file.size() > MAX_FILE_SIZE){
		QMessageBox::critical(this, tr("Load Failed"), tr("file size is %1 bytes, the limit is 10000 bytes").arg(file.size()), QMessageBox::Ok);
		return;
	}

	if (file.size() > TIP_FILE_SIZE){
		if (QMessageBox::question(this, tr("Hint"), tr("the file is too large, it will use more time, open continue?"), \
			QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
				return;
	}

	QTextStream text(&file);
	QString line;
	while (!text.atEnd()){
		line = text.readLine();
		ui->textEditSend->setText(ui->textEditSend->toPlainText() + line + "\n");
	}
	file.close();
	ui->statusBar->showMessage(tr("read data of the file successfully!"));
}

void MainWindow::on_actionCleanPort_triggered()
{
	myCom->flush();
}

void MainWindow::on_actionWriteToFile_triggered()
{
	if (ui->actionWriteToFile->isChecked()){
		QString filename = QFileDialog::getSaveFileName(this, tr("Write File"), 0, tr("save as (*.*)"));  //锟斤拷痈锟斤拷锟斤拷锟侥硷拷锟斤拷锟斤拷
		if (filename.isEmpty()){
			ui->actionWriteToFile->setChecked(false);
		}
		else{
			write2fileName = filename;
			ui->actionWriteToFile->setToolTip(tr("stop write file"));
		}
	}
	else{
		write2fileName.clear();
		ui->actionWriteToFile->setToolTip(tr("write the reading data to file"));
	}
}

void MainWindow::saveComDefaultConfig()
{
	if (m_comset){
		m_comset->setValue("ComSettings/ComName", ui->portNameComboBox->currentIndex());
		m_comset->setValue("ComSettings/BaudRate", ui->baudRateComboBox->currentIndex());
		m_comset->setValue("ComSettings/DataBit", ui->dataBitsComboBox->currentIndex());
		m_comset->setValue("ComSettings/Parity", ui->parityComboBox->currentIndex());
		m_comset->setValue("ComSettings/StopBit", ui->stopBitsComboBox->currentIndex());
	}
}

void MainWindow::on_btn1BytesCS_clicked()
{
	ui->lineEdit_1bCS->clear();
	UINT8 cs = 0x00;
	char tmpstr[4096] = { 0 };
	QString str;
	bool ok;
	UINT8 data;
	QStringList list;
	str = ui->textEditSend->toPlainText();
    list = str.split(" ");
	for (int i = 0; i < list.count(); i++) {
		if (list.at(i) == " ")
			continue;
		if (list.at(i).isEmpty())
			continue;
		data = list.at(i).toUInt(&ok, 16);
		if (!ok) {
            QMessageBox::information(this, tr("Hint"), tr("data format error!"), QMessageBox::Ok);
			if (obotimer != NULL)
				obotimer->stop();
			ui->sendmsgBtn->setText(tr("send"));
			ui->sendmsgBtn->setIcon(QIcon(":new/prefix1/src/send.png"));
			return;
		}
		cs += data;
	}

	ui->lineEdit_1bCS->setText("0x" + QString::number(cs, 16).rightJustified(2, '0').toUpper());
	str = ui->textEditSend->toPlainText();
	sprintf(tmpstr, " %02X", cs);
	str.append(tmpstr);
	ui->textEditSend->setText(str);
}

void MainWindow::on_btn2BytesCS_clicked()
{
	ui->lineEdit2BytesCS->clear();
	UINT16 cs = 0x0000;
    if (ui->chradioButton2->isChecked()){
		QString str;
		bool ok;
		UINT8 data;
		QStringList list;
		str = ui->textEditSend->toPlainText();
        list = str.split(" ");
		for (int i = 0; i < list.count(); i++){
			if (list.at(i) == " ")
				continue;
			if (list.at(i).isEmpty())
				continue;
			data = list.at(i).toUInt(&ok, 16);
			if (!ok){
                QMessageBox::information(this, tr("Hint"), tr("data format error!"), QMessageBox::Ok);
				if(obotimer != NULL)
					obotimer->stop();

				ui->sendmsgBtn->setText(tr("send"));
				ui->sendmsgBtn->setIcon(QIcon(":new/prefix1/src/send.png"));
				return;
			}
			cs += data;
		}
    } else {

	}
	ui->lineEdit2BytesCS->setText("0x" + QString::number(cs,16).rightJustified(4, '0').toUpper());
}

void MainWindow::on_btnCRC_clicked()
{
	ui->lineEditCRCResult->clear();
	QString input = ui->textEditSend->toPlainText();
	QByteArray buf;
	QStringList list;
	UINT16 calc_crc;
	QString s, output;
	bool ok;
	char data;

	if (!input.isEmpty()){
        list = input.split(" ");
		for (int i = 0; i < list.count(); i++){
			if (list.at(i) == " ")
				continue;
			if (list.at(i).isEmpty())
				continue;
			data = (char)list.at(i).toInt(&ok, 16);
			if (!ok){
                QMessageBox::information(this, tr("Hint"), tr("data format error!"), QMessageBox::Ok);
				return;
			}
			buf.append(data);
		}
		calc_crc = calcModRtuCRC((uchar *)buf.data(), buf.length());
		output.sprintf(" %02X %02X", (uchar)calc_crc, (uchar)(calc_crc >> BYTE_LENGTH));
		ui->lineEditCRCResult->setText(output);
		input.append(output);
		ui->textEditSend->setText(input);
	}
}

/* 计算ModBus-RTU传输协议的CRC校验值
* ref: http://www.ccontrolsys.com/w/How_to_Compute_the_Modbus_RTU_Message_CRC
* 生成多项式为 x^16+x^15+x^2+x^0, 即系数字串为 (1 1000 0000 0000 0101),
* 舍弃16次幂的系数后为 (1000 0000 0000 0101, 即0x8005),
* 再反序得到 (1010 0000 0000 0001, 即0xA001)
* buf, 指向消息头的指针;
* len, 消息体的长度
*/
UINT16 MainWindow::calcModRtuCRC(uchar *buf, int len)
{
	UINT16 crc = 0xFFFF;
	QString s;
	QString buffer;

	for (int pos = 0; pos < len; pos++) {
		crc ^= (UINT16)buf[pos];
		s.sprintf("0x%04X", (UINT16)crc);
		buffer.append("before shift: ").append(s).append('\n');
		for (int i = BYTE_LENGTH; i > 0; i--) {
			if (crc & 0x0001) {
				crc >>= 1;
				crc ^= POLY;
			}
			else
				crc >>= 1;
		}
		s.sprintf("0x%04X", (UINT16)crc);
		buffer.append("after shift: ").append(s).append('\n');
	}
	return crc;
}

UINT16 MainWindow::CRC16ModRTU_Tbl_Drv (const uchar *nData, UINT16 wLength)
{
	static const UINT16 wCRCTable[] = {
		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 
	};

	uchar nTemp;
	UINT16 wCRCWord = 0xFFFF;

	while (wLength--){
		nTemp = *nData++ ^ wCRCWord;
		wCRCWord >>= BYTE_LENGTH;
		wCRCWord  ^= wCRCTable[nTemp];
	}
	return wCRCWord;
}
